今天要來講的是Databinding
直譯就是資料綁定
這也是實現MVVM架構很重要的一個library
這功能簡而言之就是讓資料跟元件綁定
舉個例子 假設現在有個Textview
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv_text"
android:text="Hello World"
/>
那我今天要設定一個變數去改變tv_text顯示的數值時
我該怎麼做呢?
一般來說你會需要先找到這個TextView
所以要 findViewById()
找到以後再去變更它的數據
實際代碼大概會像這樣
onResume(){
...
String show = "show";
TextView text=(TextView) findViewByid(R.id.tv_text);
text.setText(show);
}
那如果改用databinding呢?
layout.xml
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.title}"
/>
viewModel.title 就是綁定到tv_text的數據
如果要變更顯示的值
只要直接變動viewModel的數據就行了
例如
onResume(){
...
viewModel.title = "show"
}
這樣就能變更tv_text顯示的數據了
介紹完差異後
那我們來撰寫一個實際例子
並且將它改成databinding的形式
這是今天的專案 會從master開branch開始改
solution可以看Databinding的分支
https://github.com/mars1120/jetpackMvvmDemo.git
首先開啟專案 新增一個新的activity
你可以對package 路徑點右鍵新增
如下圖
然後選Fragment+ViewModel
如果你是手動新增的話
記得確認
build.gradle(Module:app)
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0'
有新增這兩項
然後因為是從master分支開出來的
而今天只會用到PreviewActivity而已
所以記得去manifest把MainActivity註解或刪掉
AndroidManifest.xml
<application
...
>
<activity android:name=".main.PreviewActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!--<activity android:name=".main.MainActivity">-->
<!--<intent-filter>-->
<!--<action android:name="android.intent.action.MAIN"/>-->
<!--<category android:name="android.intent.category.LAUNCHER"/>-->
<!--</intent-filter>-->
<!--</activity>-->
</application>
接著去修改PreviewAcitivy
串接Fragment時會傳遞一個變數
PreviewFragment.newInstance() 改為 PreviewFragment.newInstance(1)
主頁代碼
PreviewAcitivy.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.preview_activity)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, PreviewFragment.newInstance(1))
.commitNow()
}
}
接著修改Fragment
首先修改Instance 讓他可以接參數
companion object {
val ARG_PAGE = "PAGE"
fun newInstance(param1: Int): PreviewFragment {
val fragment = PreviewFragment()
val args = Bundle()
args.putInt(ARG_PAGE, param1)
fragment.arguments = args
return fragment
}
}
接著在onActivityCreated時解析數據並顯示
...
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
//目前還沒用到
viewModel = ViewModelProviders.of(this).get(PreviewViewModel::class.java)
//接收數據
val showPage = getArguments()?.getInt(ARG_PAGE) ?: 0
//找到textview
val tv: TextView = activity!!.findViewById(R.id.message)
//修改顯示的數據
tv.text = "傳遞過來的訊息是:" + showPage.toString()
//新增click事件
tv.setOnClickListener {
Toast.makeText(context, "tv.text=" + tv.text, Toast.LENGTH_SHORT).show()
}
}
功能為:畫面中間顯示傳遞值
點選後會跳Toast顯示資訊
接著開始把專案改為databinding的形式
先去build.gradle新增對databinding的支援
build.gradle(Module:app)
...
android {
...
dataBinding.enabled = true
}
如附圖
接著打開preview_fragment.xml
鼠標移到layout外圈(此例為ConstraintLayout)
按alt+Enter 或點擊後左上有黃色燈號
點開後點選Convert to data binding layout
或參閱下圖
附一下layout的變化
接著在xml宣告資料來源
preview_fragment.xml
<data>
<variable
name="viewModel"
type="com.ithome11.jetpackmvvmdemo.main.ui.preview.PreviewViewModel"/>
</data>
name可取任意名稱
type="com.ithome11.jetpackmvvmdemo.main.ui.preview.PreviewViewModel"
為Viewmodel的路徑
修改textview的資料來源
<TextView
android:id="@+id/message"
android:text="@{`message:`+String.valueOf(viewModel.message)}"
/>
android:text="@{viewModel.title}"
viewModel根據name變動
title則是PreviewViewModel內的變數
關於更多databinding 字串的用法
可參閱這篇
https://stackoverflow.com/questions/38978499/how-do-i-use-databinding-to-combine-a-string-from-resources-with-a-dynamic-varia
需要注意的一點就是要小心大小寫跟變數不要打錯了
不然compile不會過
舉例來說我目前變數使用viewModel.title
但實際上viewModel內是甚麼都沒有的
如果現在就執行專案的話 會出現如下的錯誤
接著去修改viewmodel
PreviewViewModel.kt
class PreviewViewModel : ViewModel() {
var message :Int = 10
}
然後修改Fragment 進行綁定
private val previewViewModel: PreviewViewModel by lazy {
return@lazy ViewModelProviders.of(this).get(PreviewViewModel::class.java)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
var binding: PreviewFragmentBinding = DataBindingUtil.inflate(inflater,
R.layout.preview_fragment, container, false)
var rootView: View = binding.root
// setting values to model
binding.viewModel = previewViewModel
return rootView
}
PreviewFragmentBinding跟要綁定的layout.xml名稱有關
舉例來說
preview_fragment.xml = PreviewFragmentBinding
hello_world_item.xml = HelloWorldItemBinding
Fragment完整代碼
PreviewFragment.kt
class PreviewFragment : Fragment() {
companion object {
val ARG_PAGE = "PAGE"
fun newInstance(param1: Int): PreviewFragment {
val fragment = PreviewFragment()
val args = Bundle()
args.putInt(ARG_PAGE, param1)
fragment.arguments = args
return fragment
}
}
private val previewViewModel: PreviewViewModel by lazy {
return@lazy ViewModelProviders.of(this).get(PreviewViewModel::class.java)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
var binding: PreviewFragmentBinding = DataBindingUtil.inflate(inflater, R.layout.preview_fragment, container, false)
var rootView: View = binding.root
// setting values to model
binding.viewModel = previewViewModel
return rootView
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
previewViewModel.message = getArguments()?.getInt(ARG_PAGE) ?: 0
}
}
然後textview就會正確顯示綁定的數據了
但還有些功能還沒替換完 明天再繼續完成它
今天的代碼在此
https://github.com/mars1120/jetpackMvvmDemo/tree/Databinding